#include <iostream>
#include <sstream>
#include <string>
#include <assert.h>
#include "status.hxx"
#include "util/common.hxx"
#include "util/crc32.hxx"


static utility::PerformanceMeasure sTotal("total"), sSerialize("serialization"), sConstruct("construct");
static utility::PerformanceMeasure slogTotal("logTotal"), slogAppend("logappend"), slogCrc("crc32");


 std::string payload;

class LogRecordTest {

public:
    uint32_t m_length;
    uint32_t m_crc;
    uint64_t m_magic;

    uint32_t m_type;
    uint32_t m_offset;

    uint64_t m_scn;
    uint32_t m_index;
    uint64_t m_addrvalue;

    std::string m_dataStoreName;
    std::string m_dbPartitionName;

    uint16_t m_segIDOld;
    uint16_t m_segIDNew;

    uint32_t m_loadSizeOld;
    uint32_t m_loadSizeNew;

    std::string m_dataNew;

    uint32_t getLength() {return m_length;}
    void setLength(uint32_t len) {m_length = len;}

    uint32_t getCRC() {return m_crc;}
    void setCRC(uint32_t crc) { m_crc = crc;}

    uint64_t getScn(){return m_scn;}
    void setScn(uint64_t scn){m_scn = scn;}

    uint32_t getType() {return m_type;}
    void setType(uint32_t type){m_type = type;}

    uint32_t getOffset() {return m_offset;}
    void setOffset(uint32_t offset) {m_offset = offset;}

    uint32_t getIndex() {return m_index; }
    void setIndex(uint32_t index) {m_index = index;}

    uint64_t getAddr(){return m_addrvalue;}
    void setAddr(uint64_t addrvalue){m_addrvalue = addrvalue;}

    uint16_t getOldSegId() {return m_segIDOld;}
    void setOldSegId(uint16_t id) {m_segIDOld = id;}

    uint16_t getNewSegId() {return m_segIDNew;}
    void setNewSegId(uint16_t id) { m_segIDNew = id;}

    uint32_t getLoadSizeOld() {return m_loadSizeOld;}
    void setLoadSizeOld(uint32_t loadSizeOld) {m_loadSizeOld = loadSizeOld;}

    uint32_t getLoadSizeNew() {return m_loadSizeNew;}
    void setLoadSizeNew(uint32_t loadSizeNew) {m_loadSizeNew = loadSizeNew;}

    std::string getDsName() {return m_dataStoreName;}
    void setDsName(const std::string& name) {m_dataStoreName = name;}


    std::string getPartitionName(){return m_dbPartitionName;}
    void setPartitionName(const std::string& name) {m_dbPartitionName = name;}

    std::string getDataNew(){return m_dataNew;}
    void setDataNew(const std::string& data){m_dataNew = data;}



    uint32_t calculateRecordLength() {
    
        uint32_t len =  sizeof(m_magic) +
            sizeof(m_scn) + sizeof(m_type) +
            sizeof(m_offset) + sizeof(m_segIDOld) + sizeof(m_segIDNew) +
            sizeof(m_index) + sizeof(m_addrvalue) +
            m_dataStoreName.size() + sizeof(uint32_t) +
            m_dbPartitionName.size() + sizeof(uint32_t) +
            sizeof(m_loadSizeOld) + sizeof(m_loadSizeNew) +
            m_dataNew.size() + sizeof(uint32_t) + sizeof(m_crc);

        return len;
    }

    std::string serialize() {
    
        slogTotal.start();
        slogAppend.start();

        int pos = 0;

        payload.copy((char*)&m_length, sizeof(m_length), pos); pos += sizeof(m_length);
        
        payload.copy((char*)&m_magic, sizeof(m_magic), pos); pos +=
            sizeof(m_magic);
        
        payload.copy((char*)&m_scn, sizeof(m_scn), pos); pos +=
            sizeof(m_scn);

        payload.copy((char*)&m_type, sizeof(m_type), pos);
         pos += sizeof(m_type);     
        
        payload.copy((char*)&m_offset, sizeof(m_offset), pos);
        pos += sizeof(m_offset);

        payload.copy((char*)&m_segIDOld, sizeof(m_segIDOld), pos);
        pos += sizeof(m_segIDOld);
        
        payload.copy((char*)&m_segIDNew, sizeof(m_segIDNew), pos);
        pos += sizeof(m_segIDNew);


        payload.copy((char*)&m_loadSizeOld, sizeof(m_loadSizeOld), pos);
        pos += sizeof(m_loadSizeOld);

        payload.copy((char*)&m_loadSizeNew, sizeof(m_loadSizeNew), pos);
        pos += sizeof(m_loadSizeNew);


        payload.copy((char*)&m_index, sizeof(m_index), pos);
        pos += sizeof(m_index);
        
        
        payload.copy((char*)&m_addrvalue, sizeof(m_addrvalue), pos);
        pos += sizeof(m_addrvalue);

        uint32_t len = 0;

        len = m_dataStoreName.size();
        payload.copy((char*)&len, sizeof(len), pos);
        pos += sizeof(len);
        
        payload.copy((char*)m_dataStoreName.c_str(), m_dataStoreName.size(), pos);
        pos += sizeof(m_dataStoreName.size());

        len = m_dbPartitionName.size(); 
        payload.copy((char*)&len, sizeof(len), pos); 
        pos += sizeof(len);
        
        payload.copy((char*)m_dbPartitionName.c_str(), m_dbPartitionName.size(), pos);
        pos += sizeof(m_dbPartitionName.size());

        len = m_dataNew.size();
        payload.copy((char*)&len, sizeof(len), pos);
        pos += sizeof(len);
        
        payload.copy((char*)m_dataNew.c_str(), m_dataNew.size(), pos);
        pos += sizeof(m_dataNew.size());

        slogAppend.end(); slogCrc.start();

        //calculate crc32
        CRC32 crc;
        crc.update(payload.data(), 0, pos);
        m_crc = crc.getValue();
        payload.copy((char*)&m_crc, sizeof(m_crc), pos);

        slogCrc.end();
        slogTotal.end();

        return payload;
    }

};


LogRecordTest constructRecord(uint16_t oldSegId,
                          uint16_t newSegId,
                          uint32_t offset,
                          const std::string& storeName,
                          const std::string& partName,
                          uint32_t loadSizeOld,
                          uint32_t loadSizeNew,
                          const std::string& data) {
    
    LogRecordTest rec;

    rec.setOffset(offset);
    rec.setOldSegId(oldSegId);
    rec.setNewSegId(newSegId);
    rec.setDsName(storeName);
    rec.setPartitionName(partName);
    rec.setLoadSizeOld(loadSizeOld);
    rec.setLoadSizeNew(loadSizeNew);
    rec.setDataNew(data);

    return rec;
}


void func() {
    
    char buf[128]; memset(buf, 'y', sizeof(buf));
    CRC32::init();

    sTotal.start();

    for(int i = 0; i < 1000000; ++i) {
        sTotal.start();

        sConstruct.start();
        LogRecordTest rec = constructRecord(i, 2*i, 100*i, "datastore",
                "partitionname", 10, 1000, buf);
        sConstruct.end();

        sSerialize.start();
        std::string logRec = rec.serialize();
        sSerialize.end();

        sTotal.end();
    }

}


int main() {

    payload.resize(4096, 0);

    func();

}


